/**
 * \file LeftExpression.h
 * \brief Headers for Expression subclasses which can be assigned to
 * \author Wouter Caarls <w.caarls@tnw.tudelft.nl> 
 *
 * \verbatim
 * Copyright (c) 2005 Wouter Caarls, Delft University of Technology
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * \endverbatim
 */

#ifndef __LEFTEXPRESSION_H_INCLUDED
#define __LEFTEXPRESSION_H_INCLUDED

#include <iostream>
#include <string>

#include "Base.h"
#include "Code.h"

//class Value;
#include "Value.h"

/** \brief Identifier.
 * 
 * In PEPCI, an identifier is only associated with a Variable, because the
 * identifiers in function and strategy definitions are converted to a \c name
 * upon parsing.
 */
class Id : public LeftExpression
{
  public:
    Id() : LeftExpression(), name("") {}
    Id(Type *_type) : LeftExpression(_type), name("") {}
    Id(Type *_type, std::string _name) : LeftExpression(_type), name(_name) {}
    Id(Type *_type, char *_name) : LeftExpression(_type), name(_name) {}

    bool equals(const LeftExpression *rhs) const
    {
      const Id *crhs = dynamic_cast<const Id*>(rhs);
      if (!crhs) return false;
      if (name != crhs->name) return false;
      return true;
    }

    Id *copy()
    {
      return new Id(type, name);
    }

    std::string toString(int indentation=0);
    std::string toTerm();

    Expression *replace(Code *src, Code *rplc,
			SymbolTable *symbols, Compound *out)
    {
      if (typeid(*src) == typeid(Id) &&
          dynamic_cast<Id*>(src)->name == this->name)
	if (dynamic_cast<Expression*>(rplc))
	{
	  LOG(plog.crawl, HDR << "Replacing " << toString() << " with " << rplc->toString()  << std::endl);
	  return dynamic_cast<Expression*>(rplc->copy());
	}
	else	  
	  return new CodeValue(rplc->copy());
      else
        return this->copy();
    }

    void findStorage(SymbolTable *symbols, Compound *out,
		     std::list<Value**> &storage, std::list<Value**> &path);
    LeftExpression* buildStoragePath(SymbolTable *symbols, Compound *out);
    void residualizeStoragePath(SymbolTable *symbols);
    LeftExpression* anonimizeStoragePath(int tindex);

    Id *findId(SymbolTable *symbols);

    bool hasSideEffects() { return false; }
  public:
    std::string name; ///< Name of the identifier
};

/** \brief Pointer dereference.
 *
 * Reduces to the storage pointed to by a PointerValue.
 */
class Deref : public LeftExpression
{
  public:
    Deref() : LeftExpression(), data(NULL) {}
    Deref(Type *_type) : LeftExpression(_type), data(NULL) {}
    Deref(Type *_type, Expression *_data) : LeftExpression(_type), data(_data) {}

    bool equals(const LeftExpression *rhs) const
    {
      const Deref *crhs = dynamic_cast<const Deref*>(rhs);
      if (!crhs) return false;

      /* *** Difficult: what to do with non-leftexpression data? *** */
      LeftExpression *ldata = dynamic_cast<LeftExpression*>(data);
      const LeftExpression *rldata = dynamic_cast<const LeftExpression*>(crhs->data);
      if (!ldata || !rldata) return false;
      if (!ldata->equals(rldata)) return false;
      return true;
    }

    Deref *copy()
    {
      return new Deref(type, data->copy());
    }

    std::string toString(int indentation=0);
    std::string toTerm();

    Deref *replace(Code *src, Code *rplc,
		   SymbolTable *symbols, Compound *out)
    {
      return new Deref(type, data->replace(src, rplc, symbols, out));
    }

    void findStorage(SymbolTable *symbols, Compound *out,
		     std::list<Value**> &storage, std::list<Value**> &path);
    LeftExpression* buildStoragePath(SymbolTable *symbols, Compound *out);
    void residualizeStoragePath(SymbolTable *symbols);
    LeftExpression* anonimizeStoragePath(int tindex);

    Id *findId(SymbolTable *symbols);

    bool hasSideEffects() { return data->hasSideEffects(); }

  public:
    /** \brief Expression to be dereferenced.
     *
     * Note that this can be something different from a LeftExpression in two situations:
     * \li We're dereferencing the result of a function call
     * \li In the case of dereferencing a pointer \e constant, such as in
     *     \code *&myVariable \endcode
     */
    Expression *data;
};

class Decode : public LeftExpression
{
  public:
    Decode() : LeftExpression(), data(NULL) {}
    Decode(Type *_type) : LeftExpression(_type), data(NULL) {}
    Decode(Type *_type, Expression *_data) : LeftExpression(_type), data(_data) {}

    bool equals(const LeftExpression *rhs) const
    {
      const Decode *crhs = dynamic_cast<const Decode*>(rhs);
      if (!crhs) return false;

      /* *** Difficult: what to do with non-leftexpression data? *** */
      LeftExpression *ldata = dynamic_cast<LeftExpression*>(data);
      const LeftExpression *rldata = dynamic_cast<const LeftExpression*>(crhs->data);
      if (!ldata || !rldata) return false;
      if (!ldata->equals(rldata)) return false;
      return true;
    }

    Decode *copy()
    {
      return new Decode(type, data->copy());
    }

    std::string toString(int indentation=0);
    std::string toTerm();

    Decode *replace(Code *src, Code *rplc,
			SymbolTable *symbols, Compound *out)
    {
      return new Decode(type, data->replace(src, rplc, symbols, out));
    }

    Expression *reduce(SymbolTable *symbols, Compound *out, Use use=NotUsed);

    void findStorage(SymbolTable *symbols, Compound *out,
		     std::list<Value**> &storage, std::list<Value**> &path);
    LeftExpression* buildStoragePath(SymbolTable *symbols, Compound *out);
    void residualizeStoragePath(SymbolTable *symbols);
    LeftExpression* anonimizeStoragePath(int tindex);

    Id *findId(SymbolTable *symbols);

    bool hasSideEffects() { return data->hasSideEffects(); }

  public:
    /** \brief Expression to be decoded.
     *
     * Note that this can be something different from a LeftExpression in two situations:
     * \li We're decoding the result of a function call
     * \li In the case of decoding a code \e constant, such as in
     *     \code @'12` \endcode
     */
    Expression *data;
};

/** \brief Array Index.
 *
 * Reduces to a specific element in an array.
 */
class ArrayIndex : public LeftExpression
{
  public:
    ArrayIndex() : LeftExpression(), data(NULL) {}
    ArrayIndex(Type *_type) : LeftExpression(_type), data(NULL), index(NULL) {}
    ArrayIndex(Type *_type, LeftExpression *_data, Expression *_index) :
      LeftExpression(_type), data(_data), index(_index) {}

    bool equals(const LeftExpression *rhs) const
    {
      const ArrayIndex *crhs = dynamic_cast<const ArrayIndex*>(rhs);
      if (!crhs) return false;

      if (!data->equals(crhs->data)) return false;

      if (index->known && crhs->index->known)
      {
	Value *iindex = dynamic_cast<Value*>(index);
	const Value *irindex = dynamic_cast<const Value*>(crhs->index);

	if (iindex->castToInteger() != irindex->castToInteger())
	  return false;
      }
      else
      {
	LeftExpression *lindex = dynamic_cast<LeftExpression*>(index);
	const LeftExpression *rlindex = dynamic_cast<const LeftExpression*>(crhs->index);

	if (!lindex || !rlindex) return false;
	if (!lindex->equals(rlindex)) return false;
      }

      return true;
    }

    ArrayIndex *copy()
    {
      return new ArrayIndex(type, data->copy(), index->copy());
    }

    std::string toString(int indentation=0);
    std::string toTerm();

    ArrayIndex *replace(Code *src, Code *rplc,
			SymbolTable *symbols, Compound *out)
    {
      Expression *res = data->replace(src, rplc, symbols, out);
      if (dynamic_cast<LeftExpression*>(res))
        return new ArrayIndex(type,
			      dynamic_cast<LeftExpression*>(res),
			      index->replace(src, rplc, symbols, out));
      else
      {        
        plog.warning() << HDR << "cannot cast replacement " << res->toTerm() << " to leftexpression because it's a " << typeid(*res).name() << std::endl;
        
        delete res;
        return this->copy();
      }
    }

    void findStorage(SymbolTable *symbols, Compound *out,
		     std::list<Value**> &storage, std::list<Value**> &path);
    LeftExpression* buildStoragePath(SymbolTable *symbols, Compound *out);
    void residualizeStoragePath(SymbolTable *symbols);
    LeftExpression* anonimizeStoragePath(int tindex);

    Id *findId(SymbolTable *symbols);

    bool hasSideEffects() { return data->hasSideEffects()||index->hasSideEffects(); }

  public:
    LeftExpression *data; ///< LeftExpression to index.
      Expression *index;  ///< Expression reducing to an index.
};

/** \brief Field of a structure.
 *
 * Reduces to a specific member -- or Field -- of a structure.
 */
class Field : public LeftExpression
{
  public:
    Field() : LeftExpression(), data(NULL) {}
    Field(Type *_type) : LeftExpression(_type), data(NULL), member(NULL) {}
    Field(Type *_type, LeftExpression *_data, Code *_member) :
      LeftExpression(_type), data(_data), member(_member) {}

    bool equals(const LeftExpression *rhs) const
    {
      const Field *crhs = dynamic_cast<const Field*>(rhs);
      if (!crhs) return false;

      if (!data->equals(crhs->data)) return false;

      Id *lhsid = dynamic_cast<Id*>(member);
      Id *rhsid = dynamic_cast<Id*>(crhs->member);

      Decode *lhsd = dynamic_cast<Decode*>(member);
      Decode *rhsd = dynamic_cast<Decode*>(crhs->member);

      if (lhsid && rhsid)
      {
	if (!lhsid->equals(rhsid)) return false;
      }
      else if (lhsd && rhsd)
      {
	if (!lhsd->equals(rhsd)) return false;
      }
      else
	return false;

      return true;
    }

    Field *copy()
    {
      return new Field(type, data->copy(), member->copy());
    }

    std::string toString(int indentation=0);
    std::string toTerm();

    Field *replace(Code *src, Code *rplc,
			SymbolTable *symbols, Compound *out)
    {
      Expression *res = data->replace(src, rplc, symbols, out);
      Code *memres;

      if (dynamic_cast<Id*>(member))
	memres = member->copy();
      else
	memres = member->replace(src, rplc, symbols, out);

      if (dynamic_cast<LeftExpression*>(res) &&
	  (dynamic_cast<Id*>(memres) || dynamic_cast<Decode*>(memres)))
        return new Field(type,
			      dynamic_cast<LeftExpression*>(res), memres);
      else
      {        
        plog.warning() << HDR << "cannot cast replacement " << res->toTerm() << " or "
		       << memres->toTerm() << " to leftexpression or id" << std::endl;
        
        delete res;
	delete memres;
        return this->copy();
      }
    }

    void findStorage(SymbolTable *symbols, Compound *out,
		     std::list<Value**> &storage, std::list<Value**> &path);
    LeftExpression* buildStoragePath(SymbolTable *symbols, Compound *out);
    void residualizeStoragePath(SymbolTable *symbols);
    LeftExpression* anonimizeStoragePath(int tindex);

    Id *findId(SymbolTable *symbols);

    bool hasSideEffects() { return data->hasSideEffects(); }

  public:
    LeftExpression *data; ///< LeftExpression to take the field of.
    Code *member;           ///< Name of the field.
};

/** \brief Direct reference to a Symbol
 *
 * To avoid scoping problems, Variables are referenced directly in anonimized
 * storage paths, for example as routes for PointerValues.
 * Note that we expressly do not unwrap code anywhere in IndexedSymbol, as the
 * pointer may point to a CodeValue.
 */
class IndexedSymbol : public LeftExpression
{
  public:
    IndexedSymbol() : LeftExpression(), index(0) {}
    IndexedSymbol(Type *_type, int _index=0) : LeftExpression(_type), index(_index) {}

    bool equals(const LeftExpression *rhs) const
    {
      const IndexedSymbol *crhs = dynamic_cast<const IndexedSymbol*>(rhs);
      if (!crhs) return false;
      if (index != crhs->index) return false;
      return true;
    }

    IndexedSymbol *copy()
    {
      return new IndexedSymbol(type, index);
    }

    std::string toString(int indentation=0);
    std::string toTerm();

    void findStorage(SymbolTable *symbols, Compound *out,
		     std::list<Value**> &storage, std::list<Value**> &path);
    LeftExpression* buildStoragePath(SymbolTable *symbols, Compound *out);
    void residualizeStoragePath(SymbolTable *symbols);
    LeftExpression* anonimizeStoragePath(int tindex);

    Id *findId(SymbolTable *symbols);

    bool hasSideEffects() { return false; }

  public:
    int index; ///< Index in a SymbolTable.
};

/** \brief XTC list array index
 *
 * XTC supports indexing a list of elements from an array. 
 * We just interpret it and output it exactly as it came in.
 */
class XTCArrayIndex : public LeftExpression
{
  public:
    XTCArrayIndex() : LeftExpression(), data(NULL) {}
    XTCArrayIndex(Type *_type) : LeftExpression(_type), data(NULL), lindex(NULL), rindex(NULL) {}
    XTCArrayIndex(Type *_type, LeftExpression *_data, Expression *_lindex, Expression *_rindex) :
      LeftExpression(_type), data(_data), lindex(_lindex), rindex(_rindex) {}

    bool equals(const LeftExpression *rhs) const
    {
      const XTCArrayIndex *crhs = dynamic_cast<const XTCArrayIndex*>(rhs);
      if (!crhs) return false;

      if (!data->equals(crhs->data)) return false;

      if (lindex->known && crhs->lindex->known &&
          rindex->known && crhs->rindex->known)
      {
	Value *ilindex = dynamic_cast<Value*>(lindex);
	const Value *irlindex = dynamic_cast<const Value*>(crhs->lindex);

	if (ilindex->castToInteger() != irlindex->castToInteger())
	  return false;

	Value *irindex = dynamic_cast<Value*>(rindex);
	const Value *irrindex = dynamic_cast<const Value*>(crhs->rindex);

	if (irindex->castToInteger() != irrindex->castToInteger())
	  return false;
      }
      else
      {
	LeftExpression *llindex = dynamic_cast<LeftExpression*>(lindex);
	const LeftExpression *rllindex = dynamic_cast<const LeftExpression*>(crhs->lindex);

	if (!llindex || !rllindex) return false;
	if (!llindex->equals(rllindex)) return false;

	LeftExpression *lrindex = dynamic_cast<LeftExpression*>(rindex);
	const LeftExpression *rlrindex = dynamic_cast<const LeftExpression*>(crhs->rindex);

	if (!lrindex || !rlrindex) return false;
	if (!lrindex->equals(rlrindex)) return false;
      }

      return true;
    }

    XTCArrayIndex *copy()
    {
      return new XTCArrayIndex(type, data->copy(), lindex->copy(), rindex->copy());
    }

    std::string toString(int indentation=0);
    std::string toTerm();

    XTCArrayIndex *replace(Code *src, Code *rplc,
			SymbolTable *symbols, Compound *out)
    {
      Expression *res = data->replace(src, rplc, symbols, out);
      if (dynamic_cast<LeftExpression*>(res))
        return new XTCArrayIndex(type,
			      dynamic_cast<LeftExpression*>(res),
			      lindex->replace(src, rplc, symbols, out),
			      rindex->replace(src, rplc, symbols, out));
      else
      {        
        plog.warning() << HDR << "cannot cast replacement " << res->toTerm() << " to leftexpression" << std::endl;
        
        delete res;
        return this->copy();
      }
    }

    void findStorage(SymbolTable *symbols, Compound *out,
		     std::list<Value**> &storage, std::list<Value**> &path);
    LeftExpression* buildStoragePath(SymbolTable *symbols, Compound *out);
    void residualizeStoragePath(SymbolTable *symbols);
    LeftExpression* anonimizeStoragePath(int tindex);
    Id *findId(SymbolTable *symbols);
    bool hasSideEffects();

  public:
    LeftExpression *data; ///< LeftExpression to index.
    Expression *lindex, *rindex;  ///< Expressions reducing to left and right indices.
};

class Escape : public LeftExpression
{
  public:
    Escape() : LeftExpression(), data(NULL) {}
    Escape(Type *_type) : LeftExpression(_type), data(NULL) {}
    Escape(Type *_type, Expression *_data) : LeftExpression(_type), data(_data) {}

    bool equals(const LeftExpression *rhs) const
    {
      error("Unbound escape sequence");
      return false;
    }

    Escape *copy()
    {
      return new Escape(type, data->copy());
    }

    std::string toString(int indentation=0);
    std::string toTerm();

    Expression *replace(Code *src, Code *rplc,
			SymbolTable *symbols, Compound *out);
    Expression *reduce(SymbolTable *symbols, Compound *out, Use use=NotUsed);

    void findStorage(SymbolTable *symbols, Compound *out,
		     std::list<Value**> &storage, std::list<Value**> &path)
    {
      error("Unbound escape sequence");
    }

    LeftExpression* buildStoragePath(SymbolTable *symbols, Compound *out)
    {
      error("Unbound escape sequence");
      return copy();
    }

    void residualizeStoragePath(SymbolTable *symbols)
    {
      error("Unbound escape sequence");      
    }

    LeftExpression* anonimizeStoragePath(int tindex)
    {
      error("Unbound escape sequence");
      return copy();
    }

    Id *findId(SymbolTable *symbols);

    bool hasSideEffects() { return false; }

  public:
    /** \brief Expression to be escaped.
     */
    Expression *data;
};

Id *getRandomId(Type *type);

#endif /* __LEFTEXPRESSION_H_INCLUDED */
